package com.jason.www.code.impl;


import com.jason.www.code.CertUtil;
import com.jason.www.code.IPayClient;
import com.jason.www.code.SecureUtil;
import com.jason.www.commons.PropertyConfigurer;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.PublicKey;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.apache.commons.lang3.StringUtils.isEmpty;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;

@Component("unionpayClient")
@Slf4j
public class UnionpayClient implements IPayClient {

	@Autowired
	private PropertyConfigurer propertyConfigurer;

	public static final String frontTransUrl="https://gateway.95516.com/gateway/api/frontTransReq.do";

	@Override
	public String buildRequestUrl(Map<String, String> requestParams) {

		Map<String, String> requestData = new HashMap<String, String>();
		
		requestData.put("version", "5.0.0");   			  //版本号，全渠道默认值
		requestData.put("encoding", "utf-8"); 			  //字符集编码，可以使用UTF-8,GBK两种方式
		requestData.put("signMethod", "01");            			  //签名方法，只支持 01：RSA方式证书加密
		requestData.put("txnType", "01");               			  //交易类型 ，01：消费
		requestData.put("txnSubType", "01");            			  //交易子类型， 01：自助消费
		requestData.put("bizType", "000201");           			  //业务类型，B2C网关支付，手机wap支付

		/***商户接入参数***/
		requestData.put("merId", propertyConfigurer.getUnionpayMerid());    	          			  //商户号码，请改成自己申请的正式商户号或者open上注册得来的777测试商户号
		requestData.put("accessType", "0");             			  //接入类型，0：直连商户 
		requestData.put("orderId",requestParams.get("orderId"));             //商户订单号，8-40位数字字母，不能含“-”或“_”，可以自行定制规则		
		requestData.put("txnTime", formatDate(new Date(), "yyyyMMddHHmmss"));        //订单发送时间，取系统时间，格式为YYYYMMDDhhmmss，必须取当前时间，否则会报txnTime无效
		requestData.put("currencyCode", "156");         			  //交易币种（境内商户一般是156 人民币）
		BigDecimal totalPrice = new BigDecimal(requestParams.get("totalPrice"));
		Long money = totalPrice.multiply(new BigDecimal(100)).longValue();
		requestData.put("txnAmt", String.valueOf(money));             			      //交易金额，单位分，不要带小数点

		requestData.put("channelType", "08");



        String baseUri = requestParams.get("requestUrl");
		String returnUrl = requestParams.get("returnUrl");
		String notifyUrl = requestParams.get("notifyUrl");
        if (propertyConfigurer.getOverhttps().equalsIgnoreCase("Y")
                && baseUri.startsWith("http://")) {
            baseUri = baseUri.replace("http://", "https://");
        }
        requestData.put("backUrl", baseUri + (isNotEmpty(notifyUrl)? notifyUrl : propertyConfigurer.getNotifyUrl()));
        requestData.put("frontUrl", baseUri + (isNotEmpty(returnUrl)? returnUrl : propertyConfigurer.getReturnUrl()));

		sign(requestData,"utf-8");
		return createAutoFormHtml(UnionpayClient.frontTransUrl, requestData,"POST",null,null,false);   //生成自动跳转的Html表单;
	}

	private static String formatDate(Date date, String pattern) {
		DateFormat formatter = new SimpleDateFormat(pattern);
		return formatter.format(date);
	}

	public static String createAutoFormHtml(String reqUrl, Map<String, String> hiddens, String method,String acceptCharset,String onSubmit,boolean newWindow) {
		StringBuffer sf = new StringBuffer();
		String otherParams = (isNotEmpty(acceptCharset) ? "  accept-charset=\"" + acceptCharset + "\"":"")
				+(isNotEmpty(onSubmit) ? "  onsubmit=\"" + onSubmit + "\"":"");
		sf.append("<form id = \"pay_form\" action=\"" + reqUrl + "\" target=\"" + (newWindow?"_blank":"_self") +"\"  method=\""  + method +  "\"" + otherParams+">");
		if (null != hiddens && 0 != hiddens.size()) {
			Set<Map.Entry<String, String>> set = hiddens.entrySet();
			Iterator<Map.Entry<String, String>> it = set.iterator();
			while (it.hasNext()) {
				Map.Entry<String, String> ey = it.next();
				String key = ey.getKey();
				String value = ey.getValue();
				if (isNotEmpty(value)){
					sf.append("<input type=\"hidden\" name=\"" + key + "\" value=\"" + value + "\"/>");
				}

			}
		}
		sf.append("<script>document.forms[0].submit();</script>");
		sf.append("</form>");
		String form = sf.toString();
		log.info("银联支付参数::{}",form);
		return form;
	}

	public static String createLinkString(Map<String, String> params) {
		if (params == null || params.size() == 0){
			return "";
		}

		List<String> keys = new ArrayList<String>(params.keySet());
		//Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);
		Collections.sort(keys, new Comparator<String>() {
			@Override
			public int compare(String o1, String o2) {
				return o1.compareTo(o2);
			}
		});

		String prestr = "";
		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			if (isNotEmpty(value)){
				if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
					prestr = prestr + key + "=" + value;
				} else {
					prestr = prestr + key + "=" + value + "&";
				}
			}
		}
		return prestr;
	}


	@Override
	public Boolean verifyCallbackHmac(Map<String, String> rspData) {

		String stringSign = rspData.get("signature");
		// 从返回报文中获取certId ，然后去证书静态Map中查询对应验签证书对象
		String certId = rspData.get("certId");
		// 将Map信息转换成key1=value1&key2=value2的形式
		rspData.put("signature", "");
		String stringData = createLinkString(rspData);
		try {
			// 验证签名需要用银联发给商户的公钥证书.
			byte[] signDigest = Base64.decode(stringSign.getBytes(StandardCharsets.UTF_8));
			PublicKey publicKey = CertUtil.getValidateKey(certId);
			return SecureUtil.validateSignBySoft(publicKey, signDigest, SecureUtil.sha1X16(stringData, "utf-8"));
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();

		}
		return false;
	}
	
	/**
	 * 生成签名值(SHA1摘要算法)
	 * 
	 * @param data
	 *            待签名数据Map键值对形式
	 * @param encoding
	 *            编码
	 * @return 签名是否成功
	 */
	public static boolean sign(Map<String, String> data, String encoding) {
		if (isEmpty(encoding)) {
			encoding = "utf-8";
		}

		data.put("certId", CertUtil.getCertIdByKeyStoreMap("/certs/unionpay/898440153992782.pfx", "062710"));//多证书模式
		data.put("signature", "");
		String stringData = createLinkString(data);
		/**
		 * 签名\base64编码
		 */
		byte[] byteSign = null;
		String stringSign = null;
		try {
			// 通过SHA1进行摘要并转16进制
			byte[] signDigest = SecureUtil.sha1X16(stringData, encoding);
			byteSign = SecureUtil.base64Encode(SecureUtil.signBySoft(CertUtil.getSignCertPrivateKeyByStoreMap("/certs/unionpay/898440153992782.pfx", "062710"), signDigest));
			stringSign = new String(byteSign);
			// 设置签名域值
			data.put("signature",stringSign);
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}


}

 
